using System;
using System.Resources;
using System.Drawing;
using System.Collections;
using System.Reflection;
using System.ComponentModel;
using System.Text;
using System.IO;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using System.Xml;
using Microsoft.Samples.BizTalk.Pipelines.CustomComponent;

namespace HIPAA_Pipelines
{

    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [ComponentCategory(CategoryTypes.CATID_Any)]
    [ComponentCategory(CategoryTypes.CATID_Validate)]
    [System.Runtime.InteropServices.Guid("9d0e4103-4cce-4536-83fa-4a5040674ad6")]
    public class MessageEnrichmentComponent :
        IBaseComponent,
        Microsoft.BizTalk.Component.Interop.IComponent,
        Microsoft.BizTalk.Component.Interop.IPersistPropertyBag,
        IComponentUI
    {
        static ResourceManager resManager = new ResourceManager("HIPAA_Pipelines.MessageEnrichmentComponent", Assembly.GetExecutingAssembly());


        // Component information
        #region IBaseComponent
        public string Name { get { return "EDI Message Enrichment Component"; } }
        public string Version { get { return "1.0"; } }
        public string Description { get { return "Message Enrichment of EDI Envelope Information"; } }
        public IntPtr Icon
        {
            get
            {
                return new System.IntPtr();
            }
        }
        public System.Collections.IEnumerator Validate(object projectSystem)
        {
            return null;
        }

        #endregion

        //private System.Resources.ResourceManager resourceManager =
        //    new System.Resources.ResourceManager("MessageEnrichment", Assembly.GetExecutingAssembly());

        // Property: New Root Name
        private string _rootName;
        [
        DisplayName("Root Element Name Seed"),
        Description(@"This data forms the first half of the output document RootNode.  Example is  X12EnrichedMessage which will become X12EnrichedMessage_X12_00401_270 ")
        ]
        public string RootNameSeed
        {
            get { return _rootName; }
            set { _rootName = value; }
        }

        // Property: New Root Namespace
        private string _rootNamespace;
        [
        DisplayName("Root Element Namespace Seed"),
        Description(@"This forms the initial seed of the top root element.  Example ""http://FooBar"" will become ""http://FooBar_X12_00401_270"".  This is then promoted as part of the new message subscription contract.")
        ]
        public string RootNamespaceSeed
        {
            get { return _rootNamespace; }
            set { _rootNamespace = value; }
        }

        // Property: Stream Backing Store Type
        private string _backingStoreType;
        [
        DisplayName("Backing Store Type (Mem/File)"),
        Description(@"This sets the backing store type for the message streams.  Valid options are Mem, File, or Hybrid (random set of File or Mem).  Default is Hybrid")
        ]
        public string BackingStoreType
        {
            get { return _backingStoreType; }
            set { _backingStoreType = value; }
        }

        #region IPersistPropertyBag Members

        public void InitNew()
        {
        }

        public void GetClassID(out Guid classID)
        {
            classID = new Guid("48BEC85A-20EE-40ad-BFD0-319B59A0DDBC");
        }

        public void Load(IPropertyBag propertyBag, int errorLog)
        {
            string val = (string)ReadPropertyBag(propertyBag, "RootNameSeed");
            if (val != null) RootNameSeed = val;

            val = (string)ReadPropertyBag(propertyBag, "RootNameSpaceSeed");
            if (val != null) RootNamespaceSeed = val;

            val = (string)ReadPropertyBag(propertyBag, "BackingStoreType");
            if (val != null) BackingStoreType = val;

        }
        public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
        {
            object val = (object)RootNameSeed;
            WritePropertyBag(propertyBag, "RootNameSeed", val);

            val = (object)RootNamespaceSeed;
            WritePropertyBag(propertyBag, "RootNamespaceSeed", val);

            val = (object)BackingStoreType;
            WritePropertyBag(propertyBag, "BackingStoreType", val);
        }

        #endregion

        #region IComponent Members
        public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
        {
            // Edi document variables
            const string _EDIDocumentStartPrefix = "ns1";
            const string _EDIDocumentTransactionSetNode = "TransactionSet";

            #region ContextAndMessageType
            string systemPropertiesNamespace = @"http://schemas.microsoft.com/BizTalk/2003/system-properties";
            //Get the promoted properties
            IBaseMessageContext context = pInMsg.Context;

            //Figure out if it is a generated Ack

            string msgType = context.Read("MessageType", systemPropertiesNamespace) as string;
            //Split on message type
            char[] typeSplit = new char[] { '#' };
            string[] _splitMsgType = msgType.Split(typeSplit);
            string rootNS = _splitMsgType[0];
            string msgTypeInfo = _splitMsgType[1];

            //These values represent the complete name and namespace
            string totalNS = this.RootNamespaceSeed + "_" + msgTypeInfo;
            string totalRoot = this.RootNameSeed + "_" + msgTypeInfo;

            string ISA_Segment = context.Read("ISA_Segment", "http://schemas.microsoft.com/Edi/PropertySchema") as string;
            string GS_Segment = context.Read("GS_Segment", "http://schemas.microsoft.com/Edi/PropertySchema") as string;

            //Call out to the enrichnment method
            XmlDocument _enrichedDocument;
            switch (msgTypeInfo)
            {
                case "X12_00401_997":
                    //We need to exit here
                    return pInMsg;
                    break;
                case "X12_997_Root":
                    //We need to exit here
                    return pInMsg;
                    break;
                case "X12_TA1_Root":
                    //We need to exit here
                    return pInMsg;
                    break;
                default:
                    break;
            }
            #endregion

            string backingStore = BackingStoreType;
            if (backingStore != "Mem" && backingStore != "File")
            {
                Random randGen = new Random();
                if (randGen.Next(0, 1) == 0)
                    backingStore = "Mem";
                else
                    backingStore = "File";
            }

            #region MemStream
            if (backingStore == "Mem")
            {
                IBaseMessagePart bodyPart = pInMsg.BodyPart;

                string messageType = "";

                if (bodyPart != null)
                {
                    Stream originalStream = bodyPart.GetOriginalDataStream();
                    if (originalStream != null)
                    {
                        //New Streaming Interface
                        MemoryStream _outStream = new MemoryStream();
                        StringWriter _outboundStringWriter = null;
                        XmlTextWriter _outboundXMLTextWriter = new XmlTextWriter(_outStream, Encoding.UTF8);

                        XmlTextReader _inboundXMLTextReader = new XmlTextReader(originalStream);
                        _inboundXMLTextReader.WhitespaceHandling = WhitespaceHandling.None;
                        _inboundXMLTextReader.MoveToContent();

                        _outboundXMLTextWriter.WriteStartElement(_EDIDocumentStartPrefix, totalRoot, totalNS);

                        XmlDocument _HeaderDoc = MessageEnrichmentLibrary.EDIHeaders.SerializeX12Headers(ISA_Segment, GS_Segment);

                        _HeaderDoc.WriteTo(_outboundXMLTextWriter);

                        _outboundXMLTextWriter.WriteStartElement(_EDIDocumentTransactionSetNode);

                        while (_inboundXMLTextReader.EOF == false)
                        {
                            _outboundXMLTextWriter.WriteNode(_inboundXMLTextReader, true);
                            _inboundXMLTextReader.Read();
                        }
                        _outboundXMLTextWriter.WriteEndElement();
                        _outboundXMLTextWriter.WriteEndElement();
                        _outboundXMLTextWriter.Flush();

                        //Create MessageType
                        messageType = totalNS + "#" + totalRoot;

                        //Promote the Message type
                        pInMsg.Context.Promote("MessageType", systemPropertiesNamespace, messageType);

                        _outStream.Position = 0;
                        bodyPart.Data = _outStream;

                        pContext.ResourceTracker.AddResource(bodyPart.Data);
                        originalStream.Close();

                        return pInMsg;
                    }
                }
                pInMsg.Context.Promote("MessageType", systemPropertiesNamespace, messageType);
                return pInMsg;
            }
            #endregion

            #region IOStream
            else //(backingStore == "File")
            {
                String _prependData, _appendData;
                XmlDocument _HeaderDoc = MessageEnrichmentLibrary.EDIHeaders.SerializeX12Headers(ISA_Segment, GS_Segment);
                _prependData = "<ns0:" + totalRoot + "xmlns:ns0:\"" + totalNS + "\">";
                _prependData = _prependData + _HeaderDoc.OuterXml;
                _prependData = _prependData + "<TransactionSet>";
                _appendData = "</TransactionSet></ns0:X12EnrichedMessage_X12_00401_837_I>";


                IBaseMessagePart bodyPart = pInMsg.BodyPart;
                if (bodyPart != null)
                {
                    byte[] prependByteData = UTF8Encoding.UTF8.GetBytes(_prependData);
                    byte[] appendByteData = UTF8Encoding.UTF8.GetBytes(_appendData);
                    Stream originalStrm = bodyPart.GetOriginalDataStream();
                    Stream strm = null;

                    if (originalStrm != null)
                    {
                        strm = new FixMsgStream(originalStrm, prependByteData, appendByteData, resManager);
                        bodyPart.Data = strm;
                        pContext.ResourceTracker.AddResource(strm);
                    }
                }

                return pInMsg;
            }
            #endregion
        }
        #endregion
        #region Helper Methods

        /// <summary>
        /// Reads property value from property bag.
        /// </summary>
        /// <param name="pb">Property bag.</param>
        /// <param name="propName">Name of property.</param>
        /// <returns>Value of the property.</returns>
        private static object ReadPropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName)
        {
            object val = null;
            try
            {
                pb.Read(propName, out val, 0);
            }

            catch (ArgumentException)
            {
                return val;
            }
            catch (Exception ex)
            {
                throw new ApplicationException(ex.Message);
            }
            return val;
        }

        /// <summary>
        /// Writes property values into a property bag.
        /// </summary>
        /// <param name="pb">Property bag.</param>
        /// <param name="propName">Name of property.</param>
        /// <param name="val">Value of property.</param>
        private static void WritePropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, object val)
        {
            try
            {
                pb.Write(propName, ref val);
            }
            catch (Exception ex)
            {
                throw new ApplicationException(ex.Message);
            }
        }
        #endregion
    }
}

